﻿using Avalonia;
using Avalonia.Media;
using Project2004.Jigsaw.Extensions;
using System;
using System.Collections.Generic;
namespace Project2004.Jigsaw.Utils
{
    public enum HoleType
    {
        In, Out, Flat
    }

    public enum InsertDirection
    {
        Left, Right, Up, Down
    }

    public class JigsawConfig
    {
        public HoleType Left { get; set; }
        public HoleType Top { get; set; }
        public HoleType Right { get; set; }
        public HoleType Bottom { get; set; }

        public JigsawConfig() { }

        public JigsawConfig(HoleType left, HoleType top, HoleType right, HoleType bottom)
        {
            Left = left;
            Top = top;
            Right = right;
            Bottom = bottom;
        }

        public JigsawConfig(JigsawConfig other)
        {
            Left = other.Left;
            Top = other.Top;
            Right = other.Right;
            Bottom = other.Bottom;
        }

        public static JigsawConfig Parse(string s)
        {
            var parts = s.Split(',', StringSplitOptions.RemoveEmptyEntries);
            return new JigsawConfig(Enum.Parse<HoleType>(parts[0], true), Enum.Parse<HoleType>(parts[1], true), Enum.Parse<HoleType>(parts[2], true), Enum.Parse<HoleType>(parts[3], true));
        }

    }

    public enum JigsawPointType
    {
        Line, Arc, Point
    }

    public class JigsawPoint
    {
        public JigsawPointType Type;
        public Point Point; // 实际坐标
        public double ArcAngle;
        public Size Size;
        public SweepDirection SweepDirection;
        public bool IsLargeArc = false;

        public static JigsawPoint CreateLine(Point point) => new JigsawPoint() { Point = point, Type = JigsawPointType.Line };
        public static JigsawPoint CreateArc(Point point, Size size, double arcAngle, SweepDirection sweepDirection, bool isLargeArc) =>
            new JigsawPoint()
            {
                Point = point,
                Type = JigsawPointType.Arc,
                Size = size,
                ArcAngle = arcAngle,
                SweepDirection = sweepDirection,
                IsLargeArc = isLargeArc
            };

        public static JigsawPoint CreatePoint(Point point) => new JigsawPoint() { Point = point, Type = JigsawPointType.Point };
    }

    public interface IJigsawShapeData
    {
        Size LargeSize { get; }
        Size SmallSize { get; }
        List<JigsawPoint> JigsawPoints { get; }
        Point StartPoint { get; }

        double Extend { get; }
    }

    public class JigsawShapeData : IJigsawShapeData
    {
        #region fields
        double _space;
        double _largeRadius;
        double _theta;
        double _majorDistance;
        double _borderLen;
        double _extend;
        double _half;
        Size _smallSize;
        double _borderPart;
        #endregion

        #region props

        public Size LargeSize { get; private set; }
        public Size SmallSize { get; private set; }
        public List<Point> Points { get; private set; }
        public List<JigsawPoint> JigsawPoints { get; private set; }
        public Point StartPoint { get; private set; }

        public double Extend { get; private set; }

        #endregion

        #region ctors 
        public JigsawShapeData()
        {
            _space = 12;
            _largeRadius = 30;
            _theta = 45 * Math.PI / 180;
            _majorDistance = _space + _largeRadius;
            _borderLen = _largeRadius * 7;

            _extend = GetExtend(_majorDistance, _largeRadius);
            _half = GetHalf(_majorDistance, _largeRadius, _theta);

            _smallSize = GetSmallSize(_majorDistance, _largeRadius, _theta);

            _borderPart = _borderLen / 2 - _half;

            LargeSize = new Size(_largeRadius, _largeRadius);
            SmallSize = _smallSize;
            Extend = _extend;
            Calc();
        }

        private void Calc()
        {
            var startPoint = new Point(_extend, _extend);
            var pA = startPoint;
            var pB = pA + new Point(_borderLen, 0);
            var pC = pB + new Point(0, _borderLen);
            var pD = pC + new Point(-_borderLen, 0);

            var pAB = (pA + pB) / 2;
            var pBC = (pB + pC) / 2;
            var pCD = (pC + pD) / 2;
            var pDA = (pD + pA) / 2;

            var pAB1 = pA + new Point(_borderPart, 0);
            var pAB2 = pAB + GetUpInsectionPoint1();
            var pAB3 = pAB + GetUpInsectionPoint2();
            var pAB4 = pB - new Point(_borderPart, 0);

            var pBC1 = pB + new Point(0, _borderPart);
            var pBC2 = pBC + GetRightInsectionPoint1();
            var pBC3 = pBC + GetRightInsectionPoint2();
            var pBC4 = pC - new Point(0, _borderPart);

            var pCD1 = pC - new Point(_borderPart, 0);
            var pCD2 = pCD + GetDownInsectionPoint2();
            var pCD3 = pCD + GetDownInsectionPoint1();
            var pCD4 = pD + new Point(_borderPart, 0);

            var pDA1 = pD - new Point(0, _borderPart);
            var pDA2 = pDA + GetLeftInsectionPoint2();
            var pDA3 = pDA + GetLeftInsectionPoint1();
            var pDA4 = pA + new Point(0, _borderPart);

            //AddPoint(JigsawPoint.CreatePoint(startPoint));
            //AddPoint(JigsawPoint.CreatePoint(new Point(0, borderPart)));

            Points = new List<Point>([pA, pAB1, pAB2, pAB3, pAB4, pB, pBC1, pBC2, pBC3, pBC4, pC, pCD1, pCD2, pCD3, pCD4, pD, pDA1, pDA2, pDA3, pDA4]);

            StartPoint = startPoint;

            JigsawPoints = new List<JigsawPoint>()
        {
             JigsawPoint.CreateLine(pA),
             JigsawPoint.CreateArc(pAB1, _smallSize, 0, SweepDirection.CounterClockwise, false),
             JigsawPoint.CreateArc(pAB2, LargeSize, 0, SweepDirection.Clockwise, true),
             JigsawPoint.CreateArc(pAB3, _smallSize, 0, SweepDirection.CounterClockwise, false),
             JigsawPoint.CreateLine(pAB4),

             JigsawPoint.CreateLine(pB),
             JigsawPoint.CreateArc(pBC1, _smallSize, 0, SweepDirection.CounterClockwise, false),
             JigsawPoint.CreateArc(pBC2, LargeSize, 0, SweepDirection.Clockwise, true),
             JigsawPoint.CreateArc(pBC3, _smallSize, 0, SweepDirection.CounterClockwise, false),
             JigsawPoint.CreateLine(pBC4),

             JigsawPoint.CreateLine(pC),
             JigsawPoint.CreateArc(pCD1, _smallSize, 0, SweepDirection.CounterClockwise, false),
             JigsawPoint.CreateArc(pCD2, LargeSize, 0, SweepDirection.Clockwise, true),
             JigsawPoint.CreateArc(pCD3, _smallSize, 0, SweepDirection.CounterClockwise, false),
             JigsawPoint.CreateLine(pCD4),

             JigsawPoint.CreateLine(pD),
             JigsawPoint.CreateArc(pDA1, _smallSize, 0, SweepDirection.CounterClockwise, false),
             JigsawPoint.CreateArc(pDA2, LargeSize, 0, SweepDirection.Clockwise, true),
             JigsawPoint.CreateArc(pDA3, _smallSize, 0, SweepDirection.CounterClockwise, false),
             JigsawPoint.CreateLine(pDA4),
        };

        }

        #endregion

        #region utils

        private double GetExtend(double majorDistance, double largeRadius)
        {
            return majorDistance + largeRadius;
        }

        private double GetSmallRadius(double majorDistance, double largeRadius, double angle)
        {
            var sin = Math.Sin(angle);
            var cos = Math.Cos(angle);

            var smallRadius = (majorDistance - largeRadius * cos) / (1 + cos);
            return smallRadius;
        }

        private Size GetSmallSize(double majorDistance, double largeRadius, double angle)
        {
            var smallRadius = GetSmallRadius(majorDistance, largeRadius, angle);

            return new Size(smallRadius, smallRadius);
        }

        private double GetHalf(double majorDistance, double largeRadius, double angle)
        {
            var sin = Math.Sin(angle);
            var cos = Math.Cos(angle);
            var smallRadius = GetSmallRadius(majorDistance, largeRadius, angle);
            var x = smallRadius;
            var y = (smallRadius + largeRadius) * sin;
            return y;
        }
        #endregion

        #region utils

        private Point GetRightInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(xf, -y);
        }

        private Point GetRightInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(xf, y);
        }

        private Point GetLeftInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-xf, -y);
        }

        private Point GetLeftInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-xf, y);
        }

        private Point GetUpInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-y, -xf);
        }

        private Point GetUpInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(y, -xf);
        }

        private Point GetDownInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-y, xf);
        }

        private Point GetDownInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(y, xf);
        }

        private Size GetSmallSize()
        {
            var smallRadius = GetSmallRadius(_majorDistance, _largeRadius, _theta);

            return new Size(smallRadius * 2, smallRadius * 2);
        }

        private double GetHalf()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var smallRadius = GetSmallRadius(_majorDistance, _largeRadius, _theta);
            var x = smallRadius;
            var y = (smallRadius + _largeRadius) * sin;
            return y;
        }
        #endregion
    }

    public class ConfigJigsawShapeData : IJigsawShapeData
    {
        #region fields
        double _space;
        double _largeRadius;
        double _theta;
        double _majorDistance;
        double _borderLen;
        double _extend;
        double _half;
        Size _smallSize;
        double _borderPart;
        #endregion

        #region props

        public Size LargeSize { get; private set; }
        public Size SmallSize { get; private set; }
        public List<JigsawPoint> JigsawPoints { get; private set; }
        public Point StartPoint { get; private set; }

        public JigsawConfig JigsawConfig { get; private set; }
        public double Extend { get; private set; }

        #endregion

        #region ctors 
        public ConfigJigsawShapeData(double borderLen, JigsawConfig jigsawConfig)
        {
            _borderLen = borderLen;
            _largeRadius = borderLen / 7;
            _space = borderLen * 12 / 30 / 7;
            _theta = 45 * Math.PI / 180;
            _majorDistance = _space + _largeRadius;

            _extend = GetExtend(_majorDistance, _largeRadius);
            _half = GetHalf(_majorDistance, _largeRadius, _theta);

            _smallSize = GetSmallSize(_majorDistance, _largeRadius, _theta);

            _borderPart = _borderLen / 2 - _half;

            LargeSize = new Size(_largeRadius, _largeRadius);
            SmallSize = _smallSize;

            JigsawConfig = jigsawConfig;
            Extend = _extend;
            Calc();
        }

        private void Calc()
        {
            var startPoint = new Point(_extend, _extend);
            var pA = startPoint;
            var pB = pA + new Point(_borderLen, 0);
            var pC = pB + new Point(0, _borderLen);
            var pD = pC + new Point(-_borderLen, 0);

            var pAB = (pA + pB) / 2;
            var pBC = (pB + pC) / 2;
            var pCD = (pC + pD) / 2;
            var pDA = (pD + pA) / 2;

            var ABPoints = new List<JigsawPoint>();
            var BCPoints = new List<JigsawPoint>();
            var CDPoints = new List<JigsawPoint>();
            var DAPoints = new List<JigsawPoint>();

            #region A2B

            if (JigsawConfig.Top == HoleType.Out)
            {
                var pAB1 = pA + new Point(_borderPart, 0);
                var pAB2 = pAB + GetUpInsectionPoint1();
                var pAB3 = pAB + GetUpInsectionPoint2();
                var pAB4 = pB - new Point(_borderPart, 0);

                ABPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pA),
                    JigsawPoint.CreateArc(pAB1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pAB2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pAB3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pAB4),
                };

            }
            else if (JigsawConfig.Top == HoleType.In)
            {
                var pAB1 = pA + new Point(_borderPart, 0);
                var pAB2 = pAB + GetUpInsectionPoint1().ReverseY();
                var pAB3 = pAB + GetUpInsectionPoint2().ReverseY();
                var pAB4 = pB - new Point(_borderPart, 0);

                ABPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pA),
                    JigsawPoint.CreateArc(pAB1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pAB2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pAB3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pAB4),
                };

            }
            else if (JigsawConfig.Top == HoleType.Flat)
            {
                ABPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pA),
                };
            }
            else
            {
                throw new NotImplementedException();
            }

            #endregion

            #region B2C

            if (JigsawConfig.Right == HoleType.Out)
            {
                var pBC1 = pB + new Point(0, _borderPart);
                var pBC2 = pBC + GetRightInsectionPoint1();
                var pBC3 = pBC + GetRightInsectionPoint2();
                var pBC4 = pC - new Point(0, _borderPart);

                BCPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pB),
                    JigsawPoint.CreateArc(pBC1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pBC2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pBC3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pBC4),
                };
            }
            else if (JigsawConfig.Right == HoleType.In)
            {
                var pBC1 = pB + new Point(0, _borderPart);
                var pBC2 = pBC + GetRightInsectionPoint1().ReverseX();
                var pBC3 = pBC + GetRightInsectionPoint2().ReverseX();
                var pBC4 = pC - new Point(0, _borderPart);

                BCPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pB),
                    JigsawPoint.CreateArc(pBC1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pBC2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pBC3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pBC4),
                };
            }
            else if (JigsawConfig.Right == HoleType.Flat)
            {
                BCPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pB),
                };
            }
            else
            {
                throw new NotImplementedException();
            }

            #endregion


            #region C2D

            if (JigsawConfig.Bottom == HoleType.Out)
            {
                var pCD1 = pC - new Point(_borderPart, 0);
                var pCD2 = pCD + GetDownInsectionPoint2();
                var pCD3 = pCD + GetDownInsectionPoint1();
                var pCD4 = pD + new Point(_borderPart, 0);

                CDPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pC),
                    JigsawPoint.CreateArc(pCD1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pCD2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pCD3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pCD4),
                };
            }
            else if (JigsawConfig.Bottom == HoleType.In)
            {
                var pCD1 = pC - new Point(_borderPart, 0);
                var pCD2 = pCD + GetDownInsectionPoint2().ReverseY();
                var pCD3 = pCD + GetDownInsectionPoint1().ReverseY();
                var pCD4 = pD + new Point(_borderPart, 0);

                CDPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pC),
                    JigsawPoint.CreateArc(pCD1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pCD2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pCD3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pCD4),
                };
            }
            else if (JigsawConfig.Bottom == HoleType.Flat)
            {

                CDPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pC),
                };

            }
            else
            {
                throw new NotImplementedException();
            }

            #endregion

            #region D2A

            if (JigsawConfig.Left == HoleType.Out)
            {
                var pDA1 = pD - new Point(0, _borderPart);
                var pDA2 = pDA + GetLeftInsectionPoint2();
                var pDA3 = pDA + GetLeftInsectionPoint1();
                var pDA4 = pA + new Point(0, _borderPart);

                DAPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pD),
                    JigsawPoint.CreateArc(pDA1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pDA2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pDA3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pDA4),
                };
            }
            else if (JigsawConfig.Left == HoleType.In)
            {
                var pDA1 = pD - new Point(0, _borderPart);
                var pDA2 = pDA + GetLeftInsectionPoint2().ReverseX();
                var pDA3 = pDA + GetLeftInsectionPoint1().ReverseX();
                var pDA4 = pA + new Point(0, _borderPart);

                DAPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pD),
                    JigsawPoint.CreateArc(pDA1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pDA2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pDA3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pDA4),
                };
            }
            else if (JigsawConfig.Left == HoleType.Flat)
            {
                DAPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pD),
                };
            }
            else
            {
                throw new NotImplementedException();
            }

            #endregion

            StartPoint = startPoint;

            JigsawPoints = new List<JigsawPoint>();
            JigsawPoints.AddRange(ABPoints);
            JigsawPoints.AddRange(BCPoints);
            JigsawPoints.AddRange(CDPoints);
            JigsawPoints.AddRange(DAPoints);

        }

        #endregion

        #region utils

        private double GetExtend(double majorDistance, double largeRadius)
        {
            return majorDistance + largeRadius;
        }

        private double GetSmallRadius(double majorDistance, double largeRadius, double angle)
        {
            var sin = Math.Sin(angle);
            var cos = Math.Cos(angle);

            var smallRadius = (majorDistance - largeRadius * cos) / (1 + cos);
            return smallRadius;
        }

        private Size GetSmallSize(double majorDistance, double largeRadius, double angle)
        {
            var smallRadius = GetSmallRadius(majorDistance, largeRadius, angle);

            return new Size(smallRadius, smallRadius);
        }

        private double GetHalf(double majorDistance, double largeRadius, double angle)
        {
            var sin = Math.Sin(angle);
            var cos = Math.Cos(angle);
            var smallRadius = GetSmallRadius(majorDistance, largeRadius, angle);
            var x = smallRadius;
            var y = (smallRadius + largeRadius) * sin;
            return y;
        }
        #endregion

        #region utils

        private Point GetRightInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(xf, -y);
        }

        private Point GetRightInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(xf, y);
        }

        private Point GetLeftInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-xf, -y);
        }

        private Point GetLeftInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-xf, y);
        }

        private Point GetUpInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-y, -xf);
        }

        private Point GetUpInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(y, -xf);
        }

        private Point GetDownInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-y, xf);
        }

        private Point GetDownInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(y, xf);
        }

        private Size GetSmallSize()
        {
            var smallRadius = GetSmallRadius(_majorDistance, _largeRadius, _theta);

            return new Size(smallRadius * 2, smallRadius * 2);
        }

        private double GetHalf()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var smallRadius = GetSmallRadius(_majorDistance, _largeRadius, _theta);
            var x = smallRadius;
            var y = (smallRadius + _largeRadius) * sin;
            return y;
        }
        #endregion
    }



    public class FullConfigJigsawShapeData : IJigsawShapeData
    {
        #region statics

        ///<summary>从包含延伸的全尺寸计算单个延伸尺寸</summary>
        public static double GetExtend(double boundSize) => boundSize / 6;

        ///<summary>计算</summary>
        public static double GetBoundSizeFromBorderLen(double borderLen) => borderLen / 2 * 3;

        ///<summary>从拼图体尺寸计算延伸的尺寸</summary>
        public static double GetOutter(double borderLen) => borderLen / 4;

        #endregion

        #region fields
        double _largeRadius;
        double _theta;
        double _majorDistance;
        double _borderLen;
        double _extend;
        double _half;
        Size _smallSize;
        double _borderPart;
        #endregion

        #region props

        public Size LargeSize { get; private set; }
        public Size SmallSize { get; private set; }
        public List<JigsawPoint> JigsawPoints { get; private set; }
        public Point StartPoint { get; private set; }

        public JigsawConfig JigsawConfig { get; private set; }
        public double Extend { get; private set; }
        #endregion

        #region ctors 
        public FullConfigJigsawShapeData(double boundSize, JigsawConfig jigsawConfig)
        {
            var extend = GetExtend(boundSize);
            _extend = extend;
            _majorDistance = extend * 0.55;
            _largeRadius = extend * 0.45;
            _borderLen = boundSize - 2 * extend;

            _theta = 45 * Math.PI / 180;

            _half = GetHalf(_majorDistance, _largeRadius, _theta);

            _smallSize = GetSmallSize(_majorDistance, _largeRadius, _theta);

            _borderPart = _borderLen / 2 - _half;

            LargeSize = new Size(_largeRadius, _largeRadius);
            SmallSize = _smallSize;

            JigsawConfig = jigsawConfig;
            Extend = extend;
            Calc();
        }

        private void Calc()
        {
            var startPoint = new Point(_extend, _extend);
            var pA = startPoint;
            var pB = pA + new Point(_borderLen, 0);
            var pC = pB + new Point(0, _borderLen);
            var pD = pC + new Point(-_borderLen, 0);

            var pAB = (pA + pB) / 2;
            var pBC = (pB + pC) / 2;
            var pCD = (pC + pD) / 2;
            var pDA = (pD + pA) / 2;

            var ABPoints = new List<JigsawPoint>();
            var BCPoints = new List<JigsawPoint>();
            var CDPoints = new List<JigsawPoint>();
            var DAPoints = new List<JigsawPoint>();

            #region A2B

            if (JigsawConfig.Top == HoleType.Out)
            {
                var pAB1 = pA + new Point(_borderPart, 0);
                var pAB2 = pAB + GetUpInsectionPoint1();
                var pAB3 = pAB + GetUpInsectionPoint2();
                var pAB4 = pB - new Point(_borderPart, 0);

                ABPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pA),
                    JigsawPoint.CreateArc(pAB1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pAB2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pAB3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pAB4),
                };

            }
            else if (JigsawConfig.Top == HoleType.In)
            {
                var pAB1 = pA + new Point(_borderPart, 0);
                var pAB2 = pAB + GetUpInsectionPoint1().ReverseY();
                var pAB3 = pAB + GetUpInsectionPoint2().ReverseY();
                var pAB4 = pB - new Point(_borderPart, 0);

                ABPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pA),
                    JigsawPoint.CreateArc(pAB1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pAB2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pAB3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pAB4),
                };

            }
            else if (JigsawConfig.Top == HoleType.Flat)
            {
                ABPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pA),
                };
            }
            else
            {
                throw new NotImplementedException();
            }

            #endregion

            #region B2C

            if (JigsawConfig.Right == HoleType.Out)
            {
                var pBC1 = pB + new Point(0, _borderPart);
                var pBC2 = pBC + GetRightInsectionPoint1();
                var pBC3 = pBC + GetRightInsectionPoint2();
                var pBC4 = pC - new Point(0, _borderPart);

                BCPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pB),
                    JigsawPoint.CreateArc(pBC1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pBC2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pBC3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pBC4),
                };
            }
            else if (JigsawConfig.Right == HoleType.In)
            {
                var pBC1 = pB + new Point(0, _borderPart);
                var pBC2 = pBC + GetRightInsectionPoint1().ReverseX();
                var pBC3 = pBC + GetRightInsectionPoint2().ReverseX();
                var pBC4 = pC - new Point(0, _borderPart);

                BCPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pB),
                    JigsawPoint.CreateArc(pBC1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pBC2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pBC3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pBC4),
                };
            }
            else if (JigsawConfig.Right == HoleType.Flat)
            {
                BCPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pB),
                };
            }
            else
            {
                throw new NotImplementedException();
            }

            #endregion


            #region C2D

            if (JigsawConfig.Bottom == HoleType.Out)
            {
                var pCD1 = pC - new Point(_borderPart, 0);
                var pCD2 = pCD + GetDownInsectionPoint2();
                var pCD3 = pCD + GetDownInsectionPoint1();
                var pCD4 = pD + new Point(_borderPart, 0);

                CDPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pC),
                    JigsawPoint.CreateArc(pCD1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pCD2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pCD3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pCD4),
                };
            }
            else if (JigsawConfig.Bottom == HoleType.In)
            {
                var pCD1 = pC - new Point(_borderPart, 0);
                var pCD2 = pCD + GetDownInsectionPoint2().ReverseY();
                var pCD3 = pCD + GetDownInsectionPoint1().ReverseY();
                var pCD4 = pD + new Point(_borderPart, 0);

                CDPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pC),
                    JigsawPoint.CreateArc(pCD1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pCD2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pCD3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pCD4),
                };
            }
            else if (JigsawConfig.Bottom == HoleType.Flat)
            {

                CDPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pC),
                };

            }
            else
            {
                throw new NotImplementedException();
            }

            #endregion

            #region D2A

            if (JigsawConfig.Left == HoleType.Out)
            {
                var pDA1 = pD - new Point(0, _borderPart);
                var pDA2 = pDA + GetLeftInsectionPoint2();
                var pDA3 = pDA + GetLeftInsectionPoint1();
                var pDA4 = pA + new Point(0, _borderPart);

                DAPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pD),
                    JigsawPoint.CreateArc(pDA1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pDA2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pDA3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pDA4),
                };
            }
            else if (JigsawConfig.Left == HoleType.In)
            {
                var pDA1 = pD - new Point(0, _borderPart);
                var pDA2 = pDA + GetLeftInsectionPoint2().ReverseX();
                var pDA3 = pDA + GetLeftInsectionPoint1().ReverseX();
                var pDA4 = pA + new Point(0, _borderPart);

                DAPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pD),
                    JigsawPoint.CreateArc(pDA1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pDA2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pDA3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pDA4),
                };
            }
            else if (JigsawConfig.Left == HoleType.Flat)
            {
                DAPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pD),
                };
            }
            else
            {
                throw new NotImplementedException();
            }

            #endregion

            StartPoint = startPoint;

            JigsawPoints = new List<JigsawPoint>();
            JigsawPoints.AddRange(ABPoints);
            JigsawPoints.AddRange(BCPoints);
            JigsawPoints.AddRange(CDPoints);
            JigsawPoints.AddRange(DAPoints);

        }

        #endregion

        #region utils

        private double GetExtend(double majorDistance, double largeRadius)
        {
            return majorDistance + largeRadius;
        }

        private double GetSmallRadius(double majorDistance, double largeRadius, double angle)
        {
            var sin = Math.Sin(angle);
            var cos = Math.Cos(angle);

            var smallRadius = (majorDistance - largeRadius * cos) / (1 + cos);
            return smallRadius;
        }

        private Size GetSmallSize(double majorDistance, double largeRadius, double angle)
        {
            var smallRadius = GetSmallRadius(majorDistance, largeRadius, angle);

            return new Size(smallRadius, smallRadius);
        }

        private double GetHalf(double majorDistance, double largeRadius, double angle)
        {
            var sin = Math.Sin(angle);
            var cos = Math.Cos(angle);
            var smallRadius = GetSmallRadius(majorDistance, largeRadius, angle);
            var x = smallRadius;
            var y = (smallRadius + largeRadius) * sin;
            return y;
        }
        #endregion

        #region utils

        private Point GetRightInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(xf, -y);
        }

        private Point GetRightInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(xf, y);
        }

        private Point GetLeftInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-xf, -y);
        }

        private Point GetLeftInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-xf, y);
        }

        private Point GetUpInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-y, -xf);
        }

        private Point GetUpInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(y, -xf);
        }

        private Point GetDownInsectionPoint1()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(-y, xf);
        }

        private Point GetDownInsectionPoint2()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var xf = _majorDistance - cos * _largeRadius;
            var y = _largeRadius * sin;
            return new Point(y, xf);
        }

        private Size GetSmallSize()
        {
            var smallRadius = GetSmallRadius(_majorDistance, _largeRadius, _theta);

            return new Size(smallRadius * 2, smallRadius * 2);
        }

        private double GetHalf()
        {
            var sin = Math.Sin(_theta);
            var cos = Math.Cos(_theta);
            var smallRadius = GetSmallRadius(_majorDistance, _largeRadius, _theta);
            var x = smallRadius;
            var y = (smallRadius + _largeRadius) * sin;
            return y;
        }
        #endregion
    }
}
